from django.shortcuts import render
from django.contrib import auth
from django.contrib.auth.models import User
from django.http import JsonResponse, FileResponse
from django.contrib.auth.decorators import login_required, permission_required
from base.models import Project, Scene, File, Result, Worker, Baseline
from urllib.parse import quote
from django.core.paginator import Paginator
import json, os
from base.apps import _cached_object
from lib.execute import Execute, stop_case
from lib.filemodel import FileModel
import shutil

# Project module
@login_required
def project_index(request):
    return render(request, "base/project/index.html", {"username": request.user})

@login_required
def project_list(request):
    # Page at the front end only
    if request.method == 'GET':
        project = request.GET['project']
        owner = request.GET['owner']
        resultlist = Project.objects.filter(project__contains=project, owner__contains=owner).values("id", "project", "description", "owner", "create_time").order_by("-id")
        paginator = Paginator(resultlist, 100)      # Paging at the back end, 100 pages per page
        pages = paginator.get_page(1)               # The back end only returns the first page, which is 100 items
        items = json.loads(json.dumps(list(pages), default=str))
        return JsonResponse({"code": 0, "msg": "success", "data": items}, safe=False)

@login_required
@permission_required('project.add_project', login_url='/atp4p/project/')
def project_add(request):
    if request.method == 'POST':
        project = request.POST['project']
        if len(project) == 0:
            return JsonResponse({"code": 1, "msg": "The project name cannot be empty", "data": ""})
        name_same = Project.objects.filter(project=project)
        if name_same:
            return JsonResponse({"code": 1, "msg": "Project already exists", "data": ""})
        else:
            ips = []
            workers = request.POST.getlist('workers')
            for worker in workers:
                if len(worker) > 0:
                    # Split into ip,username,password 3 elements and return the list
                    worker = worker.split(',', 2)
                    if len(worker) == 3:
                        ips.append(worker[0])       # Concatenate the ip and save it to the worker table
                        Worker(project=project, ip=worker[0], username=worker[1], password=worker[2]).save()
                    else:
                        return JsonResponse({"code": 1, "msg": "The format of the load machine configuration is incorrect", "data": workers})
            description = request.POST['description']
            cron = request.POST['cron']
            mails = request.POST['mails']
            engine = request.POST['engine']
            owner = "kony"
            workers = ','.join(ips)
            # Check that the cron expression is correct and create the job
            flag = _cached_object.create_job(project, cron)
            if flag is False:
                return JsonResponse({"code": 1, "msg": "The cron expression is incorrect", "data": cron})
            else:
                prj = Project(project=project, description=description, cron=cron, mails=mails, engine=engine, owner=owner, workers=workers)
                prj.save()
                # 如果创建失败，比如已经存在或者没权限，应该处理下，或者接口报错，数据没回滚
                os.makedirs(f'../projects/{project}/scripts')
                os.makedirs(f'../projects/{project}/data/{project}/tmp')
                os.makedirs(f'../projects/{project}/results')
                os.makedirs( f'../projects/{project}/results/logs' )
                project = Project.objects.filter(id=prj.id).values("id", "project", "description", "cron", "mails", "engine", "workers", "owner", "create_time")
                return JsonResponse({"code": 0, "msg": "success", "data": list(project)})
    return render(request, "base/project/add.html", {"username": request.user})

@login_required
def project_delete(request):
    project = request.GET['project']
    if len(project) == 0:
        return JsonResponse({"code": 1, "msg": "The project name cannot be empty", "data": ""})
    is_run = Project.objects.filter(project=project, status=1)
    if is_run:
        return JsonResponse({"code": 1, "msg": "The project is already in running", "data": ""})
    else:
        Project.objects.filter(project=project).delete()
        Scene.objects.filter(project=project).delete()
        Result.objects.filter(project=project).delete()
        File.objects.filter(project=project).delete()
        Worker.objects.filter(project=project).delete()
        shutil.rmtree(f'../projects/{project}')
        _cached_object.delete_job(project)
        return JsonResponse({"code": 0, "msg": "success", "data": ""})

@login_required
def project_update(request):
    if request.method == 'POST':
        project = request.POST['project']
        is_exit = Project.objects.filter(project=project)
        if is_exit:
            ips = []
            Worker.objects.filter(project=project).delete()
            workers = request.POST.getlist('workers')
            for worker in workers:
                if len(worker) > 0:
                    worker = worker.split(',')
                    if len(worker) == 3:
                        # Concatenate the ip and save it to the worker table
                        ips.append(worker[0])
                        Worker(project=project, ip=worker[0], username=worker[1], password=worker[2]).save()
                    else:
                        return JsonResponse({"code": 1, "msg": "负载机配置格式有误", "data": workers})
            description = request.POST['description']
            cron = request.POST['cron']
            mails = request.POST['mails']
            engine = request.POST['engine']
            workers = ','.join(ips)
            # Check that the cron expression is correct and create the job
            flag = _cached_object.update_job(project, cron)
            if flag is False:
                return JsonResponse( {"code": 1, "msg": "The cron expression is incorrect", "data": cron} )
            else:
                Project.objects.filter(project=project).update(description=description, cron=cron, mails=mails, engine=engine, workers=workers)
                return JsonResponse({"code": 0, "msg": "success", "data": ""})

# View project details
@login_required
def project_query(request):
    if request.method == 'GET':
        project = request.GET['project']
        prj = Project.objects.get(project=project)
        worker_list = []
        # Returns a list with only data, and the list elements are tuples
        workers = Worker.objects.filter(project=project).values_list("ip", "username", "password")
        workers = list(workers)
        for worker in workers:
            worker_str = ','.join(worker)
            worker_list.append(worker_str)
        return render(request, "base/project/query.html", {"prj": prj, "workers": worker_list, "username": request.user})

@login_required
def project_run(request):
    if request.method == 'GET':
        project = request.GET['project']
        prj_conf = Project.objects.filter(project=project).values("mails", "workers", "engine",  "status")
        status = prj_conf[0]["status"]
        if status == 1:
            return JsonResponse({"code": 1, "msg": "The project is already in running", "data": ""})
        else:
            execute = Execute(project)
            run_result = execute.run_case(model="project")
            return JsonResponse(run_result)

@login_required
def project_stop(request):
    if request.method == 'GET':
        project = request.GET['project']
        prj_conf = Project.objects.filter(project=project).values("status", "ident")
        status = prj_conf[0]["status"]
        if status == 0:
            return JsonResponse({"code": 1, "msg": "The project is not running", "data": ""})
        else:
            ident = prj_conf[0]["ident"]
            result = stop_case(project, ident, SystemExit)
            return JsonResponse(result)
        
# Test scenario module
@login_required
def scene_index(request):
    return render(request, "base/scene/index.html", {"username": request.user})

@login_required
def scene_list(request):
    if request.method == 'GET':
        project = request.GET['project']
        result_list = Scene.objects.filter(project__contains=project).values("id", "project", "scene_type", "trans_name", "threads").order_by("-id")
        paginator = Paginator(result_list, 100)
        pages = paginator.get_page(1)
        items = json.loads(json.dumps(list(pages), default=str))
        return JsonResponse({"code": 0, "msg": "success", "data": items}, safe=False)

@login_required
def scene_add(request):
    if request.method == 'POST':
        project = request.POST['project']
        name_same = Project.objects.filter(project=project)
        if name_same:
            trans_name = request.POST['trans_name']
            scene_type = request.POST['scene_type']
            threads = request.POST['threads']
            duration = request.POST['duration']
            threshold = request.POST['threshold']
            params = request.POST['params']
            Scene(project=project, trans_name=trans_name, scene_type=scene_type, threads=threads, duration=duration, threshold=threshold, params=params).save()
            return JsonResponse({"code": 0, "msg": "success", "data": ""})
        else:
            return JsonResponse({"code": 1, "msg": "The project does not exist", "data": ""})
    return render(request, "base/scene/add.html", {"username": request.user})

@login_required
def scene_delete(request):
    if request.method == 'GET':
        prj_id = request.GET['id'].split(",")
        Scene.objects.filter(id__in=prj_id).delete()
        return JsonResponse({"code": 0, "msg": "success", "data": ""})

@login_required
def scene_update(request):
    if request.method == 'POST':
        conf_id = request.POST['id']
        id_exit = Scene.objects.filter(id=conf_id)
        if id_exit:
            trans_name = request.POST['trans_name']
            scene_type = request.POST['scene_type']
            threads = request.POST['threads']
            duration = request.POST['duration']
            threshold = request.POST['threshold']
            params = request.POST['params']
            Scene.objects.filter(id=conf_id).update(trans_name=trans_name, scene_type=scene_type, threads=threads, duration=duration, threshold=threshold, params=params)
            return JsonResponse({"code": 0, "msg": "success", "data": ""})

@login_required
def scene_query(request):
    if request.method == 'GET':
        conf_id = request.GET['id']
        conf = Scene.objects.get(id=conf_id)
        return render(request, "base/scene/query.html", {"conf": conf, "username": request.user})

@login_required
def scene_run(request):
    if request.method == 'GET':
        project = request.GET['project']
        scene_id = request.GET['id']
        prj_conf = Project.objects.filter(project=project).values("mails", "workers", "engine",  "status")
        status = prj_conf[0]["status"]
        if status == 1:
            return JsonResponse({"code": 1, "msg": "The project is already in running", "data": ""})
        else:
            execute = Execute(project)
            run_result = execute.run_case(model="scene", scene_id=scene_id)
            return JsonResponse(run_result)

# Test file module
@login_required
def file_index(request):
    if request.method == 'GET':
        project = request.GET['project']
        file_list = File.objects.filter(project=project).values("id", "file_name", "path", "upload_time")
        return render(request, "base/file/index.html", {"file_list": file_list, "project": project, "username": request.user})

@login_required
def file_upload(request):
    if request.method == "POST":
        project = request.GET['project']
        # Gets the uploaded file, or defaults to None if there is no file
        files = request.FILES.getlist("file", None)
        if not files:
            return JsonResponse({"code": 1, "msg": "No files for upload", "project": project})
        else:
            for file in files:
                filename = file.name
                if ".py" in filename or ".jmx" in filename:
                    filepath = f'../projects/{project}/scripts/{file.name}'
                else:
                    filepath = f'../projects/{project}/data/{project}/{file.name}'
                abspath = os.path.abspath(filepath)
                File.objects.update_or_create(defaults={'project': project, 'file_name': filename, 'path': abspath}, path=abspath)
                f = open(abspath, "wb+")
                # Write to the file in blocks
                for chunk in file.chunks():
                    f.write(chunk)
                f.close()
            return JsonResponse({"code": 0, "msg": "success", "project": project})

@login_required
def file_download(request):
    if request.method == 'GET':
        file_id = request.GET['id']
        file_result = File.objects.filter(id=file_id)
        if file_result:
            file = list(file_result)[0]
            # File name and path
            name = file.file_name
            path = file.path
            file = open(path, 'rb')
            response = FileResponse(file)
            # Encode the file name using quote
            response['Content-Disposition'] = 'attachment;filename="%s"' % quote(name)
            return response
        else:
            return JsonResponse({"code": 1, "msg": "File does not exist", "data": ""})

@login_required
def file_delete(request):
    if request.method == 'GET':
        file_id = request.GET['id'].split(",")
        records = File.objects.filter(id__in=file_id).values("path")
        records = list(records)
        File.objects.filter(id__in=file_id).delete()
        for record in records:     
            path = record['path']
            os.remove(path)
        return JsonResponse({"code": 0, "msg": "success", "data": ""})

@login_required
def file_sync(request):
    if request.method == 'GET':
        project = request.GET['project']
        file_id = request.GET['id'].split(",")
        records = File.objects.filter(id__in=file_id).values("path")
        prj = Project.objects.filter(project=project).values("engine", "workers")
        engine = prj[0]['engine']
        workers = prj[0]['workers']
        filesync = FileModel(project, engine, workers)
        msg = filesync.sync(list(records))
        return JsonResponse(msg)

@login_required
def file_split(request):
    if request.method == 'GET':
        project = request.GET['project']
        file_id = request.GET['id']
        records = File.objects.filter(id=file_id).values("path")
        prj = Project.objects.filter(project=project).values("engine", "workers")
        engine = prj[0]['engine']
        workers = prj[0]['workers']
        filesync = FileModel(project, engine, workers)
        msg = filesync.split(list(records))
        return JsonResponse(msg)

# Test result module
@login_required
def result_index(request):
    if request.method == 'GET':
        return render(request, "base/result/index.html", {"username": request.user})

@login_required
def result_list(request):
    # At present, pagination in the front end, page is not useful, temporarily retained
    if request.method == 'GET':
        project = request.GET['project']
        trans = request.GET['trans']
        page = int(request.GET['page'])
        resultlist = Result.objects.filter(project__contains=project, tran_name__contains=trans).values("id", "project", "tran_name", "file_name", "create_time").order_by("-id")
        paginator = Paginator(resultlist, 200)
        if page < 2:
            pages = paginator.get_page(1)
        else:
            pages = paginator.get_page(page)
        items = json.loads(json.dumps(list(pages), default=str))
        return JsonResponse({"code": 0, "msg": "success", "data": items}, safe=False)

@login_required
def result_download(request):
    if request.method == 'GET':
        files_id = request.GET['id']
        files = Result.objects.filter(id=files_id).values("file_name", "path")
        # 文件名称及路径
        name = files[0]['file_name']
        path = files[0]['path']
        file = open(path, 'rb')
        response = FileResponse(file)
        # 使用quote对文件名称进行编码
        response['Content-Type'] = 'application/octet-stream'
        response['Content-Disposition'] = 'attachment;filename="%s"' % quote(name)
        return response

@login_required
def result_delete(request):
    if request.method == 'GET':
        # Multiple files are supported and need to be converted to a list
        result_id = request.GET['id'].split(",")
        records = Result.objects.filter(id__in=result_id).values("path")
        records = list(records)
        Result.objects.filter(id__in=result_id).delete()
        for record in records:     
            path = record['path']
            os.remove(path)
        return JsonResponse({"code": 0, "msg": "success", "data": ""})

# baseline
@login_required
def base_index(request):
    if request.method == 'GET':
        return render(request, "base/baseline/index.html", {"username": request.user})

@login_required
def base_list(request):
    # At present, pagination in the front end, page is not useful, temporarily retained
    if request.method == 'GET':
        project = request.GET['project']
        trans = request.GET['trans']
        page = int(request.GET['page'])
        resultlist = Baseline.objects.filter(project__contains=project, tran_name__contains=trans).values("id", "project", "tran_name", "thread", "tps", "response_time").order_by("-id")
        paginator = Paginator(resultlist, 200)
        if page < 2:
            pages = paginator.get_page(1)
        else:
            pages = paginator.get_page(page)
        items = json.loads(json.dumps(list(pages), default=str))
        return JsonResponse({"code": 0, "msg": "success", "data": items}, safe=False)

@login_required
def base_delete(request):
    if request.method == 'GET':
        result_id = request.GET['id'].split(",")
        Baseline.objects.filter(id__in=result_id).delete()
        return JsonResponse({"code": 0, "msg": "success", "data": ""})

@login_required
def base_add(request):
    if request.method == 'POST':
        project = request.POST['project']
        tran_name = request.POST['trans_name']
        thread = request.POST['thread']
        name_same = Project.objects.filter(project=project)
        if name_same:
            is_exist = Baseline.objects.filter(project=project, tran_name=tran_name, thread=thread)
            if is_exist:
                return JsonResponse( {"code": 1, "msg": "The baseline data already exists", "data": ""} )
            else:
                tps = request.POST['tps']
                response_time = request.POST['response_time']
                Baseline(project=project, tran_name=tran_name, thread=thread,tps=tps, response_time=response_time).save()
                return JsonResponse( {"code": 0, "msg": "success", "data": ""} )
        else:
            return JsonResponse({"code": 1, "msg": "The project does not exist.", "data": ""})
    return render(request, "base/baseline/add.html", {"username": request.user})

@login_required
def base_update(request):
    if request.method == 'POST':
        project = request.POST['project']
        tran_name = request.POST['trans_name']
        thread = request.POST['thread']
        tps = request.POST['tps']
        response_time = request.POST['response_time']
        is_exit = Baseline.objects.filter( project=project, tran_name=tran_name )
        if is_exit:
            is_exit.update(thread=thread, tps=tps, response_time=response_time)
            return JsonResponse( {"code": 0, "msg": "success", "data": ""} )
        else:
            return JsonResponse({"code": 1, "msg": "Record does not exist.", "data": ""})

@login_required
def base_query(request):
    if request.method == 'GET':
        base_id = request.GET['id']
        conf = Baseline.objects.get(id=base_id)
        return render(request, "base/baseline/query.html", {"conf": conf, "username": request.user})

# User module
@login_required
def user_index(request):
    return render(request, "base/user/index.html", {"username": request.user})

@login_required
def user_list(request):
    if request.method =='GET':
        name = request.GET['name']
        userlist = User.objects.filter(username__contains=name).values("id", "username", "email", "is_superuser").order_by("id")
        paginator = Paginator(userlist, 100)
        users = paginator.get_page(1)
        items = json.loads(json.dumps(list(users), default=str))
        return JsonResponse({"code": 0, "msg": "success", "data": items}, safe=False)

@login_required
def user_add(request):
    if request.method =='POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        email = request.POST.get('email')
        is_superuser = int(request.POST.get('is_superuser'))
        same_user = User.objects.filter(username=username)
        if same_user:
            return JsonResponse({"code": 1, "msg": "The user already exists", "data": ""})
        elif is_superuser:
            if email:
                # email is required when creating a superuser
                User.objects.create_superuser(username=username, password=password, email=email)
            else:
                return JsonResponse({"code": 1, "msg": "Please enter email address", "data": ""})
        else:
            # Create a common User
            User.objects.create_user(username=username, password=password)
        return JsonResponse({"code": 0, "msg": "success", "data": ""})
    return render(request, 'base/user/add.html', {"username": request.user})

@login_required
def user_del(request):
    if request.method =='GET':
        user_id = request.GET['id'].split(",")
        User.objects.filter(id__in=user_id).delete()
        return JsonResponse({"code": 0, "msg": "success", "data": ""})

@login_required
def user_set(request):
    if request.method =='POST':
        old_password = request.POST.get('old_pwd')
        new_password = request.POST.get('new_pwd')
        confirm_new_password = request.POST.get('confirm_new_pwd')
        if new_password == confirm_new_password:
            is_right = request.user.check_password(old_password)  # Automatically encrypt and verify passwords
            if is_right:
                # This step is to modify the properties of the object
                request.user.set_password(new_password)
                # This step is the actual operational data
                request.user.save()
            else:
                return JsonResponse({"code": 1, "msg": "Incorrect password", "data": ""})
        else:
            return JsonResponse({"code": 1, "msg": "Confirm password is inconsistent with the new password", "data": ""})
        return JsonResponse({"code": 0, "msg": "success", "data": ""})

# Log in
def login(request):
    if request.method =='POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        # Verify that the account and password is correct
        user = auth.authenticate(request, username=username, password=password)
        # Check whether the current user exists
        if user:
            # Save the user status if the user exists
            # Similar to request.session[key]=user, saved to the django_session table
            auth.login(request, user)
            # Once this method is executed, you can get the currently logged user object from anywhere through request.user
            return JsonResponse({"code": 0, "msg": "success", "data": ""})
        else:
            return JsonResponse({"code": 1, "msg": "The user name or password is incorrect", "data": ""})
    return render(request, 'base/login/index.html')

@login_required
def logout(request):
    auth.logout(request)  # Delete the user information from the django_session table
    return render(request, 'index.html')
